using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Diagnostics;

using AForge.Imaging;
using AForge.Imaging.Filters;
using AForge.Math;
using AForge.Vision;
using AForge.Vision.Motion;

namespace Pg.BioMedics.RatObserver.ImageProcessing
{
    public class ImageProcessor
    {
        #region Fields

        private IMotionDetector _detector;
        private IFilter _mirror;
        private int _numberOfRegionsX = 1;
        private int _numberOfRegionsY = 1;

        private List<double> _motionLevels;
        private List<double[]> _regionMotionLevels;

        #endregion

        #region Constructors

        public ImageProcessor()
        {
            _detector = new Pg.BioMedics.RatObserver.ImageProcessing.TwoFramesDifferenceMotionDetector(true, true);
            //_detector = new BackgroundModelingLowPrecisionMotionDetector(true);
            //_detector = new BackgroundModelingHighPrecisionMotionDetector(true, true);
            _mirror = new Mirror(true, false);
            _motionLevels = new List<double>();
            _regionMotionLevels = new List<double[]>();
        }

        #endregion 
       
        #region Properties

        public MotionDetectionMethod MotionDetectionMethod
        {
            get
            {
                lock (_detector)
                {
                    if (_detector is TwoFramesDifferenceMotionDetector)
                        return MotionDetectionMethod.Basic;
                    else
                        return MotionDetectionMethod.Adaptive;
                }
            }

            set
            {
                lock (_detector)
                {
                    if (value == MotionDetectionMethod.Basic)
                        _detector = new TwoFramesDifferenceMotionDetector(true, true);
                    else
                        _detector = new BackgroundModelingHighPrecisionMotionDetector(true, true);
                }
            }
        }

        public double[] MotionLevels
        {
            get
            {
                return _motionLevels.ToArray();
            }
        }

        public double[][] RegionMotionLevels
        {
            get
            {
                return _regionMotionLevels.ToArray();
            }
        }

        public int NumberOfRegionsX
        {
            get { return _numberOfRegionsX;  }
            set
            {
                if (value > 0)
                {
                    _numberOfRegionsX = value;
                    (_detector as TwoFramesDifferenceMotionDetector).RegionsX = value;
                }
            }
        }

        public int NumberOfRegionsY
        {
            get { return _numberOfRegionsY; }
            set
            {
                if (value > 0)
                {
                    _numberOfRegionsY = value;

                    (_detector as TwoFramesDifferenceMotionDetector).RegionsY = value;
                }
            }
        }

        #endregion

        #region Methods

        public Bitmap Process(Bitmap srcImg)
        {           
            _detector.ProcessFrame(srcImg);
            _motionLevels.Add(_detector.MotionLevel);
            _regionMotionLevels.Add((_detector as TwoFramesDifferenceMotionDetector).RegionsMotionLevels);

            return _mirror.Apply(srcImg);
        }

        public double[] FilterLevels(double[] levels, params int[] movingAvgFilters)
        {
            //double[] result = new double[levels.Length];
            //for (int i = 0; i < levels.Length; i++)
            //    result[i] = levels[i];

            //foreach (int filter in movingAvgFilters)
            //{
            //    MovingAverageCalculator maCalc = new MovingAverageCalculator(filter);

            //    double[] tmpResult = new double[levels.Length];
            //    for (int i = 0; i < levels.Length; i++)
            //        tmpResult[i] = maCalc.NextValue(result[i]);

            //    for (int i = 0; i < levels.Length; i++)
            //        result[i] = tmpResult[i];
            //}

            //return result;

            MovingAverageCalculator maCalc = new MovingAverageCalculator(20);
            MovingAverageCalculator maCalc2 = new MovingAverageCalculator(5);
            MovingAverageCalculator maCalc3 = new MovingAverageCalculator(10);

            double[] tmpResult = new double[levels.Length];
            for (int i = 0; i < levels.Length; i++)
            {
                tmpResult[i] = maCalc.NextValue(levels[i]);
            }

            double[] tmpResult2 = new double[levels.Length];
            for (int i = 0; i < levels.Length; i++)
            {
                tmpResult2[i] = maCalc2.NextValue(tmpResult[i]);
            }

            double[] result = new double[levels.Length];
            for (int i = 0; i < levels.Length; i++)
            {
                result[i] = maCalc3.NextValue(tmpResult[i]);
            }

            return result;
        }

        public int CountRotations(double[] values, double diffLevel)
        {
            double[] diff = Diff(values);

            int result = 0;
            double lastMax = 0;
            double lastMin = 0;
            for (int i = 1; i < diff.Length - 1; i++)
            {
                if ((diff[i - 1] > 0) && (diff[i] < 0))
                {
                    lastMax = values[i];

                    if (lastMin < (lastMax - diffLevel * lastMax))
                        result++;
                }
                else if ((diff[i - 1] < 0) && (diff[i] > 0))
                {
                    lastMin = values[i];
                }
            }

            return result;
        }

        private double[] Diff(double[] values)
        {
            double[] result = new double[values.Length];
            for (int i = 1; i < result.Length - 1; i++)
            {
                result[i] = (values[i + 1] - values[i - 1]) / 2;
            }

            return result;
        }

        #endregion
    }

    public enum MotionDetectionMethod
    {
        Basic,
        Adaptive
    }
}
